/*
* Kodkod -- Copyright (c) 2005-present, Emina Torlak
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package kodkod.doc;
import java.util.Map;
import com.sun.javadoc.Doc;
import com.sun.javadoc.MethodDoc;
import com.sun.javadoc.Tag;
import com.sun.tools.doclets.formats.html.markup.RawHtml;
import com.sun.tools.doclets.internal.toolkit.Content;
import com.sun.tools.doclets.internal.toolkit.taglets.InheritableTaglet;
import com.sun.tools.doclets.internal.toolkit.taglets.Taglet;
import com.sun.tools.doclets.internal.toolkit.taglets.TagletWriter;
import com.sun.tools.doclets.internal.toolkit.util.DocFinder;
import com.sun.tools.doclets.internal.toolkit.util.DocletConstants;
/**
* Represents a taglet for specification tags:
* <code>@specfield</code>, <code>@invariant</code>,
* <code>@ensures</code>, and <code>@requires</code>.
*
* @specfield name: String
*
* @author Emina Torlak
*/
public final class SpecificationTaglet implements InheritableTaglet {
private static final int TYPE = 1, FIELD = 2, METHOD = 4, CONSTRUCTOR = 8, PACKAGE = 16, OVERVIEW = 32;
private final String tagName, tagHeader;
private int tagLocations;
/**
* Constructs a specification taglet for the given tag name, using the
* given header and location specifier.
*/
private SpecificationTaglet(String tagName, String tagHeader, int tagLocations) {
this.tagName = tagName;
this.tagHeader = tagHeader;
this.tagLocations = tagLocations;
}
/**
* Register this Taglet.
* @param tagletMap the map to register this tag to.
*/
public static void register(Map<String, Taglet> tagletMap) {
register(tagletMap, new SpecificationTaglet("specfield", "Specfields:", TYPE));
register(tagletMap, new SpecificationTaglet("requires", "Requires:", METHOD+CONSTRUCTOR));
register(tagletMap, new SpecificationTaglet("ensures", "Ensures:", METHOD+CONSTRUCTOR));
register(tagletMap, new SpecificationTaglet("invariant", "Invariants:", TYPE+METHOD+CONSTRUCTOR+FIELD));
}
/**
* Adds the given taglet to the map.
*/
private static void register(Map<String, Taglet> tagletMap, Taglet taglet) {
final Taglet current = tagletMap.get(taglet.getName());
if (current != null)
tagletMap.remove(taglet.getName());
tagletMap.put(taglet.getName(), taglet);
}
/**
* {@inheritDoc}
* @see com.sun.tools.doclets.internal.toolkit.taglets.Taglet#getName()
*/
@Override
public String getName() { return tagName; }
/**
* {@inheritDoc}
*/
@Override
public void inherit(DocFinder.Input input, DocFinder.Output output) {
if (input.element != null && input.element.isMethod()) {
final Tag[] tags = input.element.tags(tagName);
if (tags.length > 0) {
output.holder = input.element;
output.holderTag = tags[0];
output.inlineTags = input.isFirstSentence ?
tags[0].firstSentenceTags() : tags[0].inlineTags();
}
}
}
/**
* Appends the formatted definition header to the given buffer.
* @return out
*/
private StringBuilder writeHeader(StringBuilder out) {
return out.append(DocletConstants.NL)
.append("<DT><B>").append(tagHeader).append("</B></DT>");
}
/**
* Appends the formatted definition tag to the given buffer.
* @return out
*/
private StringBuilder writeTag(StringBuilder out, Tag tag, TagletWriter writer) {
return out.append(DocletConstants.NL)
.append("<DD><CODE>")
.append(writer.commentTagsToOutput(tag, null, tag.inlineTags(), false))
.append("</CODE></DD>");
}
/**
* {@inheritDoc}
*/
@Override
public Content getTagletOutput(Tag tag, TagletWriter writer) throws IllegalArgumentException {
final StringBuilder out = writeTag(writeHeader(new StringBuilder()), tag, writer);
return new RawHtml(out.toString());
}
/**
* {@inheritDoc}
*/
@Override
public Content getTagletOutput(Doc doc, TagletWriter writer) throws IllegalArgumentException {
Tag[] tags = doc.tags(getName());
if (tags.length==0 && doc instanceof MethodDoc) { // inherit if necessary and possible
final DocFinder.Output inheritedDoc = DocFinder.search(new DocFinder.Input((MethodDoc) doc, this));
tags = inheritedDoc.holderTag == null ? tags : new Tag[] {inheritedDoc.holderTag};
}
if (tags.length==0)
return null;
final StringBuilder out = writeHeader(new StringBuilder());
for(Tag tag : tags) {
writeTag(out, tag, writer);
}
return new RawHtml(out.toString());
}
/**
* {@inheritDoc}
*/
@Override
public boolean inConstructor() { return (tagLocations & CONSTRUCTOR) != 0; }
/**
* {@inheritDoc}
*/
@Override
public boolean inField() { return (tagLocations & FIELD) != 0; }
/**
* {@inheritDoc}
*/
@Override
public boolean inMethod() { return (tagLocations & METHOD) != 0; }
/**
* {@inheritDoc}
*/
@Override
public boolean inOverview() { return (tagLocations & OVERVIEW) != 0; }
/**
* {@inheritDoc}
*/
@Override
public boolean inPackage() { return (tagLocations & PACKAGE) != 0; }
/**
* {@inheritDoc}
*/
@Override
public boolean inType() { return (tagLocations & TYPE) != 0; }
/**
* {@inheritDoc}
*/
@Override
public boolean isInlineTag() { return false; }
}